package com.zerocarbon.framework.db.config;

import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import com.zerocarbon.framework.db.injector.MySqlInjector;
import com.zerocarbon.framework.db.mybatis.WriteInterceptor;
import com.zerocarbon.framework.db.mybatis.typehandler.*;
import com.zerocarbon.framework.db.parsers.DynamicTableNameParser;
import com.zerocarbon.framework.db.properties.DatabaseProperties;
import com.zerocarbon.framework.db.properties.MultiTenantType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;

import java.util.ArrayList;
import java.util.List;

/**
 * mybatis-plus配置， 常用重用拦截器，zerocarbon.db.multiTenantType=任意模式 都需要实例出来
 * 拦截器执行一定是： WriteInterceptor > DataScopeInterceptor > PaginationInterceptor
 * @author wyf  2020年6月9日
 */
@Import(DateMetaObjectHandler.class)
@EnableConfigurationProperties(DatabaseProperties.class)
@Slf4j
public class DefaultMybatisPlusConfig {

    @Autowired
    private DatabaseProperties databaseProperties;
    
    @Autowired
    private TenantHandler tenantHandler;
    
    @Autowired
    private ISqlParserFilter iSqlParserFilter;
    
    @Value("${spring.profiles.active}")
    protected String profiles;

    @Value("${zerocarbon.filemanager.imageServerRootUrl}")
    private String urlPrefix;


    /**
     * 演示环境权限拦截器
     * @return
     */
    @Bean
    @Order(15)
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "zerocarbon.db.isNotWrite", havingValue = "true")
    public WriteInterceptor getWriteInterceptor() {
        return new WriteInterceptor();
    }

    /**
     * 分页插件，自动识别数据库类型
     * 多租户，请参考官网【插件扩展】
     */
    @Order(5)
    @Bean
    public PaginationInterceptor paginationInterceptor() {
    	//log.info("---------------------------17 PaginationInterceptor init----------------------------------------");
    	PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDialectType("mysql"); // TODO
        List<ISqlParser> sqlParserList = new ArrayList<>();
        
        //攻击 SQL 阻断解析器 加入解析链
        if (databaseProperties.getIsBlockAttack()) { 
            sqlParserList.add(new BlockAttackSqlParser());
        }
        log.info("profiles:" + profiles);
        log.info("{}，multiTenantType:{}, ignoreTables:{}, ignoreSqls:{}", databaseProperties.getTenant().getMultiTenantType().getDescribe(),
        		databaseProperties.getTenant().getMultiTenantType().name(), databaseProperties.getTenant().getIgnoreTables(), databaseProperties.getTenant().getIgnoreSqls());
        
        if (MultiTenantType.COLUMN.eq(databaseProperties.getTenant().getMultiTenantType())) {  //是否开启多租户隔离（租户字段）
        	log.info("TenantIdColumn:" + tenantHandler.getTenantIdColumn());
            TenantSqlParser tenantSqlParser = new TenantSqlParser().setTenantHandler(tenantHandler);
            sqlParserList.add(tenantSqlParser);
            paginationInterceptor.setSqlParserFilter(iSqlParserFilter);
        } else if (MultiTenantType.SCHEMA.eq(databaseProperties.getTenant().getMultiTenantType())) {  //动态"表名" 插件 来实现 租户schema切换 加入解析链
            DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser(databaseProperties.getTenant().getTenantDatabasePrefix());
            paginationInterceptor.setSqlParserList(sqlParserList);
            sqlParserList.add(dynamicTableNameParser);
        } 
        paginationInterceptor.setSqlParserList(sqlParserList);
        return paginationInterceptor;
    }
    
    /**
     * Mybatis Plus 注入器 ，通过上面import实现了
     * @return
     *//*
    @Bean("myMetaObjectHandler")
    @ConditionalOnMissingBean
    public MetaObjectHandler getMyMetaObjectHandler() {
        //DatabaseProperties.Id id = databaseProperties.getId();
        return new DateMetaObjectHandler(id.getWorkerId(), id.getDataCenterId());
    }*/
    
    /**
     * Mybatis 自定义的类型处理器： 处理XML中  #{name,typeHandler=leftLike} 类型的参数
     * 用于左模糊查询时使用
     * eg：
     * and name like #{name,typeHandler=leftLike}
     * @return
     */
    @Bean
    public LeftLikeTypeHandler getLeftLikeTypeHandler() {
        return new LeftLikeTypeHandler();
    }
    
    /**
     * Mybatis 自定义的类型处理器： 处理XML中  #{name,typeHandler=rightLike} 类型的参数
     * 用于右模糊查询时使用
     * eg：
     * and name like #{name,typeHandler=rightLike}
     * @return
     */
    @Bean
    public RightLikeTypeHandler getRightLikeTypeHandler() {
        return new RightLikeTypeHandler();
    }

    /**
     * Mybatis 自定义的类型处理器： 处理XML中  #{name,typeHandler=fullLike} 类型的参数
     * 用于全模糊查询时使用
     * eg：
     * and name like #{name,typeHandler=fullLike}
     * @return
     */
    @Bean
    public FullLikeTypeHandler getFullLikeTypeHandler() {
        return new FullLikeTypeHandler();
    }
    
    /**
     * Mybatis 自定义的类型处理器：处理XML中  #{name,typeHandler=fieldEncrypt} 类型的参数
     * 用于存储需要加密的字段，存储加密，查询解密
     * eg：
     * and name = #{name,typeHandler=fieldEncrypt}
     * @return
     */
    @Bean
    public FieldEncryptTypeHandler<?> FieldEncryptTypeHandler() {
        return new FieldEncryptTypeHandler();
    }

    /**
     * 用户信息字段加密
     * Mybatis 自定义的类型处理器：处理XML中  #{name,typeHandler=fieldAccountEncrypt} 类型的参数
     * 用于存储需要加密的字段，存储加密，查询解密
     * eg：
     * and name = #{name,typeHandler=fieldAccountEncrypt}
     * @return
     */
    @Bean
    public FieldAccountEncryptTypeHandler getFieldAccountEncryptTypeHandler(){
        return new FieldAccountEncryptTypeHandler();
    }
    
    /**
     * Mybatis 自定义的类型处理器：处理XML中  #{name,typeHandler=fieldMapJson} 类型的参数
     * 用于存储Json格式字符串的字段，存储序列化成json，查询反序列化成javaBean对象
     * eg：
     * and name = #{name,typeHandler=fieldMapJson}
     * @return
     */
    @Bean
    public FieldMapJsonTypeHandler getFieldJsonTypeHandler() {
        return new FieldMapJsonTypeHandler();
    }

    @Bean
    public UrlPrefixTypeHandler getUrlPrefixTypeHandler() {
        return new UrlPrefixTypeHandler(urlPrefix);
    }
    
    /**
     * 自定义sql 注入器
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public MySqlInjector getMySqlInjector() {
        return new MySqlInjector();
    }
    
    /**
     * 打印 sql，性能分析拦截器，不建议生产使用
     * 设置 dev test 环境开启
     */
    /*@Bean
    @Profile({"dev","test"})
    public PerformanceInterceptor performanceInterceptor() {
        return new PerformanceInterceptor();
    }*/
    
    /*@Bean
    public TenantWebMvcConfigurer getTenantWebMvcConfigurer() {
        return new TenantWebMvcConfigurer();
    }*/
}
